package com.abocide.appium.sdk.core;

import com.abocide.appium.sdk.core.xml.Node;
import com.abocide.appium.sdk.core.xml.Page;
import com.abocide.appium.sdk.exception.TimeoutException;
import com.abocide.appium.sdk.factory.PageFactory;
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.MobileBy;
import io.appium.java_client.TouchAction;
import io.appium.java_client.touch.WaitOptions;
import io.appium.java_client.touch.offset.PointOption;
import org.openqa.selenium.By;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.time.Duration;
import java.util.List;

public class SdkDriver {

    private static final Logger log = LoggerFactory.getLogger(SdkDriver.class);

    /**
     * 设备
     */
    private AppiumDriver driver;
    /**
     * 默认等待时间
     */
    private WebDriverWait defaultWait;


    public SdkDriver(AppiumDriver driver) {
        this(driver, new WebDriverWait(driver, 10));
    }

    public SdkDriver(AppiumDriver driver, WebDriverWait defaultWait) {
        this.driver = driver;
        this.defaultWait = defaultWait;
    }

    public AppiumDriver getDriver() {
        return driver;
    }

    public void setDriver(AppiumDriver driver) {
        this.driver = driver;
    }

    public WebDriverWait getDefaultWait() {
        return defaultWait;
    }

    public void setDefaultWait(WebDriverWait defaultWait) {
        this.defaultWait = defaultWait;
    }

    public WebElement byId(WebDriverWait wait, String id) {
        return wait.until(ExpectedConditions.visibilityOfElementLocated(By.id(id)));
    }

    public WebElement byId(String id) {
        return byId(defaultWait, id);
    }

    public List<WebElement> byClass(WebDriverWait wait, String className) {
        return wait.until(ExpectedConditions.visibilityOfAllElementsLocatedBy(By.className(className)));
    }

    public List<WebElement> byClass(String className) {
        return defaultWait.until(ExpectedConditions.visibilityOfAllElementsLocatedBy(By.className(className)));
    }

    public WebElement byXpath(WebDriverWait wait, String xpath) {
        By locator = By.xpath(xpath);
        return wait.until(ExpectedConditions.visibilityOfElementLocated(locator));
    }

    public WebElement byXpath(String xpath) {
        return byXpath(this.defaultWait, xpath);
    }

    public WebElement byAccessibilityId(String accessibilityId) {
        return byAccessibilityId(defaultWait, accessibilityId);
    }

    public WebElement byAccessibilityId(WebDriverWait wait, String accessibilityId) {
        return wait.until(ExpectedConditions.visibilityOfElementLocated(MobileBy.AccessibilityId(accessibilityId)));
    }

    public Page getPage() {
        return PageFactory.build(this.driver.getPageSource());
    }

    public Page getPageWaitContent(String content, boolean isLike, long timeoutMillis) {
        long startTime = System.currentTimeMillis();
        long remainingTime = timeoutMillis;

        while (remainingTime > 0) {
            Page page = PageFactory.build(this.driver.getPageSource());
            List<Node> list = isLike ? page.getListByLike(content) : page.getListByContent(content);
            if (!CollectionUtils.isEmpty(list)) {
                return page;
            }

            // 暂停一段时间（例如100毫秒），以避免过于频繁地检查
            try {
                Thread.sleep(100); // 你可以根据需要调整这个值
            } catch (InterruptedException e) {
                // 处理中断异常，例如重新设置中断状态
                Thread.currentThread().interrupt();
                // 如果需要，你也可以在这里抛出运行时异常
                // throw new RuntimeException("Thread was interrupted", e);
            }

            // 计算剩余时间
            long currentTime = System.currentTimeMillis();
            remainingTime = timeoutMillis - (currentTime - startTime);
        }

        // 如果超时，可以抛出一个异常或者返回一个特定的值（例如null）
        // 这里选择抛出一个自定义异常
        throw new TimeoutException("Failed to find the page with content within the specified timeout.");
    }

    public Page getPageWaitContent(String content) {
        return getPageWaitContent(content, true, 30000);
    }

    public void click(Node node, Long timeout) {
        Node.Location location = node.getLocation();
        int x = location.getLeft().add(location.getRight().subtract(location.getLeft()).divide(BigDecimal.valueOf(2), 0)).intValue();
        int y = location.getTop().add(location.getBottom().subtract(location.getTop()).divide(BigDecimal.valueOf(2), 0)).intValue();
        try {
            Thread.sleep(timeout);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        touch(x, y);
        try {
            Thread.sleep(timeout);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void click(Node node) {
        click(node, 2000L);
    }

    public SdkDriver touch(int x, int y) {
        TouchAction touchAction = new TouchAction(this.driver);
        touchAction.tap(PointOption.point(x, y)).perform();
        return this;
    }

    public SdkDriver slide(int start, int end, int millis) {

        Dimension size = this.driver.manage().window().getSize();
        // 获取屏幕的 X 轴长度
        int x = size.getWidth() / 2;
        new TouchAction(driver).press(PointOption.point(x, start)) // 按下操作，指定起始坐标
                .waitAction(WaitOptions.waitOptions(Duration.ofMillis(millis))) // 等待500毫秒，模拟滑动速度
                .moveTo(PointOption.point(x, end)) // 移动到目标坐标
                .release() // 释放操作
                .perform();// 执行触摸动作序列
        return this;
    }

    public SdkDriver slide(int start, int end) {
        return slide(start, end, 5000);
    }


    public Node getNodeWaitContent(String content) {
        List<Node> nodeWaitContent = getNodeWaitContent(content, false, 20000);
        return nodeWaitContent.get(0);
    }

    public Node getNodeWaitContent(String content, long timeoutMillis) {
        List<Node> nodeWaitContent = getNodeWaitContent(content, false, timeoutMillis);
        return nodeWaitContent.get(0);
    }


    public List<Node> getNodeListWaitContent(String content) {
        return getNodeListWaitContent(content, 20000);
    }

    public List<Node> getNodeListWaitContent(String content, long timeoutMillis) {
        return getNodeWaitContent(content, true, timeoutMillis);
    }


    public List<Node> getNodeWaitContent(String content, boolean isLike, long timeoutMillis) {
        long startTime = System.currentTimeMillis();
        long remainingTime = timeoutMillis;

        while (remainingTime > 0) {
            Page page = PageFactory.build(this.driver.getPageSource());
            List<Node> list = isLike ? page.getListByLike(content) : page.getListByContent(content);
            if (!CollectionUtils.isEmpty(list)) {
                return list;
            }

            // 暂停一段时间（例如100毫秒），以避免过于频繁地检查
            try {
                Thread.sleep(100); // 你可以根据需要调整这个值
            } catch (InterruptedException e) {
                // 处理中断异常，例如重新设置中断状态
                Thread.currentThread().interrupt();
                // 如果需要，你也可以在这里抛出运行时异常
                // throw new RuntimeException("Thread was interrupted", e);
            }

            // 计算剩余时间
            long currentTime = System.currentTimeMillis();
            remainingTime = timeoutMillis - (currentTime - startTime);
        }

        // 如果超时，可以抛出一个异常或者返回一个特定的值（例如null）
        // 这里选择抛出一个自定义异常
        throw new TimeoutException("Failed to find the page with content within the specified timeout.");
    }

}
